package com.example.shirodemo.authentication;

import com.example.core.utils.JwtUtils;
import com.example.core.utils.RedisUtil;
import com.example.shirodemo.entities.Permission;
import com.example.shirodemo.entities.Role;
import com.example.shirodemo.entities.User;
import com.example.shirodemo.services.IPermissionService;
import com.example.shirodemo.services.IRoleService;
import com.example.shirodemo.services.IUserService;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class JwtShiroRealm extends AuthorizingRealm {
    @Autowired
    IUserService userService;
    @Autowired
    IRoleService roleService;
    @Autowired
    IPermissionService permissionService;
    @Autowired
    JwtUtils jwtUtils;
    @Autowired
    RedisUtil redisUtil;

    /**
     * 一定要重写support()方法，在后面的身份验证器中会用到
     *
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 授权模块，获取用户角色和权限
     *
     * @param principal
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        User user = (User) SecurityUtils.getSubject().getPrincipal();
        String userIdStr = String.valueOf(user.getId());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        if (redisUtil.hmExists(RedisUtil.USER_REDIS_ROLES, userIdStr)
                && redisUtil.hmExists(RedisUtil.USER_REDIS_PERMISSIONS, userIdStr)) {
            // 缓存中获取角色和权限
            Set<String> roleSet = (Set<String>) redisUtil.hmGet(RedisUtil.USER_REDIS_ROLES, userIdStr);
            Set<String> permissionSet = (Set<String>) redisUtil.hmGet(RedisUtil.USER_REDIS_PERMISSIONS, userIdStr);

            simpleAuthorizationInfo.setRoles(roleSet);
            simpleAuthorizationInfo.setStringPermissions(permissionSet);
        } else {
            // 获取用户角色集
            List<Role> roleList = this.roleService.findUserRoleByUserId(user.getId());
            Set<String> roleSet = roleList.stream().map(Role::getRoleName).collect(Collectors.toSet());
            simpleAuthorizationInfo.setRoles(roleSet);

            // 获取用户权限集
            List<Permission> permissionList = this.permissionService.findUserPermissionsByUserId(user.getId());
            Set<String> permissionSet = permissionList.stream().map(Permission::getPerms).collect(Collectors.toSet());
            simpleAuthorizationInfo.setStringPermissions(permissionSet);

            // 存入redis
            redisUtil.hmSet(RedisUtil.USER_REDIS_ROLES, userIdStr, roleSet);
            redisUtil.hmSet(RedisUtil.USER_REDIS_PERMISSIONS, userIdStr, permissionSet);
        }

        return simpleAuthorizationInfo;
    }

    /**
     * 用户认证
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        // 获取token
        String jwtToken = (String) token.getPrincipal();

        Claims claims = jwtUtils.getClaimByToken(jwtToken);//解密
        if (claims == null) {
            throw new UnknownAccountException("登录过期，请重新登录！");
        }

        // 获取用户id
        String userId = String.valueOf(claims.getSubject());

        //redis防止重复登录
        Object tokenObj = redisUtil.hmGet(RedisUtil.USER_REDIS_LOGIN, userId);
        if (tokenObj == null || !StringUtils.equals(jwtToken, String.valueOf(tokenObj))) {
            throw new UnknownAccountException("登录过期，请重新登录！");
        }

        User user = userService.getById(userId);

        if (user == null)
            throw new UnknownAccountException("登录过期，请重新登录！");
        if (User.STATUS_LOCK == user.getStatus())
            throw new LockedAccountException("账号已被锁定,请联系管理员！");

        return new SimpleAuthenticationInfo(user, jwtToken, getName());
    }
}
